/**
 * @brief 日志进程
 * @version 1.0
 * @author 宋炜
 */
#include <unistd.h>
#include <string>
#include <memory>

#include <signal.h>
#include <getopt.h>

#include "misc.hpp"
#include "debug.hpp"
#include "version.hpp"
#include "pipe.hpp"
#include "unixsock.hpp"
#include "app.hpp"
#include "log.hpp"

using namespace wheels;

struct stContext
{
    std::string    m_path;          // 日志文件路径
    std::string    m_type;          // IPC类型
    std::string    m_unix;          // unix socket名称
    int            m_page_size;     // 分页尺寸，每一页保存一条LOG。如果日志超过页面大小则通过折行处理到下一页面
    int            m_count;         // 页面数量

    std::shared_ptr< ipcItfc >  pt_svr__;
};

// 默认参数，在默认情况下保存24M内容
stContext gCtx = {"./etc/log.tinyf","P", "tinyLogSock" , 256, 100000, nullptr };

void printVer()
{
    std::cout << "tinyLog \n"
            << VERSION::MAIN_VER << "."
            << VERSION::SEC_VER << "."
            << VERSION::COMPILER_VER << "."
            << VERSION::RE_VER
            << std::endl;
}

void printHelp()
{
    std::cout << " -h        输出帮助信息\n"
              << " -v        输出版本信息\n"
              << " -n <文件> 日志文件名称\n"
              << " -t <类型> IPC类型，支持PIPE，unixsocket两种， P 默认管道；U unixsocket\n"
              << " -u <名称> unixsocket节点名称, 使用unixsocket的时候有效\n"
              << " -p <大小> 每条日志的最大尺寸,单位为字节\n"
              << " -c <数量> 最大保存日志数量，日志会循环覆盖\n"
              << " -e <文件> 输出成.txt文件\n"
              << " -C <文件> 清理日志\n"
              << std::endl;
}
/**
 * @brief 输出成文本文件
 * @param path 输出文件路径
 */
void doExport( const std::string& path )
{
    auto ptlog = log::create_shared( gCtx.m_path , gCtx.m_page_size , gCtx.m_count );
    if( !ptlog){
        ERROR_MSG( "日志模块无效" );
        debug::printCallstack();
        return;
    }

    if( ptlog->toText( path ) ){
        MSG_1( "文件 %s 输出成功" , OK_MSG , path.c_str() );
    }else{
        MSG_1( "文件 %s 输出失败" , TRED , path.c_str() );
    }

    return;
}
/**
 * @brief 清理日志
 */
void doClear( const std::string& path )
{
    auto ptlog = log::create_shared( gCtx.m_path , gCtx.m_page_size , gCtx.m_count );
    if( !ptlog){
        ERROR_MSG( "日志模块无效" );
        return;
    }
    ptlog->clear();
}
/**
 * @brief 解析命令行参数
 */
bool parseCmd( int argc , char * const argv[] )
{
    bool ret = true;

    int c = 0;

    while( ( c = getopt( argc , argv , "hvn:t:u:p:c:e:C:" ) ) != -1 ){
        switch( c ){
        case 'h':
            printVer();
            printHelp();
            return false;
        case 'v':
            printVer();
            return false;
        case 'n':{
            if( optarg ){
                gCtx.m_path = optarg;
            }else{
                ERROR_MSG( "文件路径不能为空" );
                printHelp();
                return false;
            }
        }break;
        case 't':{
            if( optarg ){
                gCtx.m_type = optarg;
            }else{
                ERROR_MSG( "IPC类型不能为空" );
                printHelp();
                return false;
            }
        } break;
        case 'u':{
            if( optarg ){
                gCtx.m_unix = optarg;
            }else{
                ERROR_MSG( "UNIXSOCKET名称不能为空" );
                printHelp();
                return false;
            }
        }break;
        case 'p':{
            if( optarg ){
                std::stringstream ss;
                ss << optarg;
                ss >> gCtx.m_page_size;
                if( ss.fail() ){
                    ERROR_MSG( "页面大小无效，默认使用256" );
                    printHelp();
                    return false;
                }
            }else{
                ERROR_MSG( "页面大小无效，默认使用256" );
                printHelp();
                return false;
            }
        }
        break;
        case 'c':{
            if( optarg ){
                std::stringstream ss;
                ss << optarg;
                ss >> gCtx.m_count;
                if( ss.fail() ){
                    ERROR_MSG( "数量无效，默认使用100000" );
                    printHelp();
                    return false;
                }
            }else{
                ERROR_MSG( "数量无效，默认使用100000" );
                printHelp();
                return false;
            }
        }break;
        case 'e':{
            if( optarg ){
                doExport( optarg );
                return false;
            }
        }
        break;
        case 'C':{
            if( optarg ){
                doClear( optarg );
            }
            return false;
        }break;
        default:
            printHelp();
            return false;
    }
    }
    return ret;
}
/**
 * @brief 初始化IPC通道
 */
bool initServer()
{
    try{
        if( gCtx.m_type == "P" ){
            auto ptr = std::make_shared< pipeSvr >();
            gCtx.pt_svr__ = std::dynamic_pointer_cast< ipcItfc >( ptr );
            return true;
        }else if( gCtx.m_type == "U" ){
            auto ptr = std::make_shared< unixSock >( gCtx.m_unix );
            gCtx.pt_svr__ = std::dynamic_pointer_cast< ipcItfc >( ptr );
            return true;
        }
    }catch( std::bad_alloc& e ){
        ERROR_MSG( e.what() );
    }catch( std::runtime_error& e ){
        ERROR_MSG( e.what() );
    }
    ERROR_MSG( "创建IPC通道失败" );
    return false;
}

/**
 * @brief 处理信号
 */
void onCtrlC( int sig )
{
    (void)sig;
    auto pt_app = app_t::get_shared();
    if(pt_app ){
        pt_app->start( false );

        close(STDIN_FILENO);
    }
}
/**
 * @brief 记录数据
 */
void onData( const variant& data )
{
    std::string d = data.get< std::string >();
    if( d.empty() == false ){
        auto ptlog = log::get_shared();
        if( !ptlog ){
            ERROR_MSG( "日志模块无效" );
            return;
        }
        if( ptlog->push( d ) == false ){
            ERROR_MSG( "添加日志内容失败" );
        }
    }
}

int start()
{
    int ret = 0;
    // 前期准备
    if( initServer() == false ){
        ERROR_MSG( "初始化IPC通道失败" );
        return -1;
    }
    auto pt_app = app_t::create_shared();
    if( !pt_app ){
        ERROR_MSG( "创建APP模块失败" );
        return -1;
    }
    dispatch_t * dspt = pt_app->getDispatch();

    auto ptlog = log::create_shared( gCtx.m_path , gCtx.m_page_size , gCtx.m_count );
    if( !ptlog ){
        ERROR_MSG( "创建日志模块失败" );
        return -1;
    }
    dspt->connect( emCmdType::DATA , onData );

    gCtx.pt_svr__->start( true );
    // 启动命令循环，程序会阻塞到这里。当按下ctrl+c后会停止运行
    pt_app->start( true );
    // 善后处理
    MSG( "------------------" , TBLUE );
    gCtx.pt_svr__->start( false );
    return ret;
}

int main( int argc , char * const argv[] )
{
    int ret = 0;

    signal( SIGINT , onCtrlC );

    if( parseCmd( argc , argv ) == true ){
        try{
            ret = start();
        }catch( std::bad_alloc& e ){
            ERROR_MSG( e.what() );
            ret = -1;
        }catch( std::runtime_error& e ){
            ERROR_MSG( e.what() );
            ret = -2;
        }
    }
    return ret;
}